package com.lzx.es.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.lzx.common.utils.R;
import com.lzx.es.entity.articleVO.Articles;
import com.lzx.es.entity.articleVO.PageResult;
import com.lzx.es.entity.articleVO.RequestParams;
import com.lzx.es.entity.userVO.QueryUser;
import com.lzx.es.feign.ArticleClient;
import com.lzx.es.service.ESArticleService;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.elasticsearch.xcontent.XContentType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author 程序员星星
 * @date 2023/2/9
 * @Description
 */
@Service
public class ESArticleServiceImpl implements ESArticleService {
    @Resource
    private RestHighLevelClient restHighLevelClient;

    @Resource
    private ArticleClient articleClient;

    //全文检索
    @Override
    public R searchArticle(RequestParams requestParams) {
        try {
            // 1.准备request
            SearchRequest request = new SearchRequest("article");
            // 2.准备DSL
            //关键字搜索
            String key = requestParams.getKey();
//            if(key==null||"".equals(key)){
            if (!StringUtils.isNoneBlank(key)) {
                request.source().query(QueryBuilders.matchAllQuery());
            } else {
                request.source().query(QueryBuilders.matchQuery("all", key));
                // 高亮
                request.source().highlighter(new HighlightBuilder().field("title").field("labelTranslation").requireFieldMatch(false));
//                HighlightBuilder highlightBuilder = new HighlightBuilder();
//                request.source().highlighter(highlightBuilder
//                        .field("title")
//                        .field("labelTranslation")
//                        .preTags("<div style=\"color: red;\">")
//                        .postTags("</div>")
//                        .requireFieldMatch(false));
            }
            //排序条件
//            if(requestParams.getSortBy()!=null||!requestParams.getSortBy().equals("")){
            if (StringUtils.isNoneBlank(requestParams.getSortBy())) {
                request.source().sort(requestParams.getSortBy(), SortOrder.DESC);
            }
            // 2.2.分页
            Integer page = requestParams.getPage();
            Integer size = requestParams.getSize();
            request.source().from((page - 1) * size).size(size);
            // 3.发送请求，得到响应
            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
            // 4.结果解析
            PageResult pageResult = handleResponse(response);
            return R.ok().data("pageResult", pageResult);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    //将数据重新导入es
    @Override
    public R saveArticleList() {
        try {
            List<Articles> articlesList = articleClient.getArticleListToES();
            //先清除es中的数据
            for (Articles articles : articlesList) {
                // 1.准备Request      // DELETE /problem/_doc/{id}
                DeleteRequest deleteRequest = new DeleteRequest("article", articles.getId().toString());
                // 2.发送请求
                restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
            }
            // 1.创建Request
            BulkRequest request = new BulkRequest();
            // 2.准备参数
            for (Articles articles : articlesList) {
                //自动补全参数数组
                List<String> arr = new ArrayList<>();
                arr.add(articles.getTitle());
                //添加自动补全参数
                articles.setSuggestion(arr);
                request.add(new IndexRequest("article")
                        .id(articles.getId().toString())
                        .source(JSON.toJSONString(articles), XContentType.JSON));
            }
            // 3.发送请求
            restHighLevelClient.bulk(request, RequestOptions.DEFAULT);
            return R.ok().message("数据重新导入成功");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    //自动补全
    @Override
    public R getSuggestion(String key) {
        try {
            // 1.准备请求
            SearchRequest request = new SearchRequest("article");
            // 2.请求参数
            request.source().suggest(new SuggestBuilder()
                    .addSuggestion(
                            "suggestions",
                            SuggestBuilders
                                    .completionSuggestion("suggestion")
                                    .size(10)
                                    .skipDuplicates(true) //不重复
                                    .prefix(key)
                    ));
            // 3.发出请求
            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
            // 4.解析
            Suggest suggest = response.getSuggest();
            // 4.1.根据名称获取结果
            CompletionSuggestion suggestion = suggest.getSuggestion("suggestions");
            // 4.2.获取options
            List<String> list = new ArrayList<>();
            for (CompletionSuggestion.Entry.Option option : suggestion.getOptions()) {
                // 4.3.获取补全的结果
                String str = option.getText().toString();
                // 4.4.放入集合
                list.add(str);
            }
            return R.ok().data("list", list);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    //添加文章
    @Override
    public R addArticle(Long articleId) {
        try {
            //查询文章
            Articles articles = articleClient.getArticleToOther(articleId);
            //创建请求
            IndexRequest request = new IndexRequest("article");
            //设置文档id
            request.id(articles.getId().toString());
            //准备Json文档
            request.source(JSON.toJSONString(articles), XContentType.JSON);
            //发送请求
            restHighLevelClient.index(request, RequestOptions.DEFAULT);
            return R.ok().message("添加成功");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    //删除文章
    @Override
    public R deleteArticle(Long articleId) {
        try {
            //准备Request
            DeleteRequest request = new DeleteRequest("article", articleId.toString());
            //发送请求
            restHighLevelClient.delete(request, RequestOptions.DEFAULT);
            return R.ok().message("删除成功");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    //修改文章
    @Transactional
    @Override
    public R editArticle(Long articleId) {
        try {
            //查询文章
            Articles articles = articleClient.getArticleToOther(articleId);
            //准备Request
            UpdateRequest request = new UpdateRequest("article", articles.getId().toString());
            //准备请求参数
            request.doc(JSONObject.toJSONString(articles), XContentType.JSON);
            //发送请求
            restHighLevelClient.update(request, RequestOptions.DEFAULT);
            return R.ok().message("修改成功");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    //查询文章
    @Override
    public R getArticle(Long articleId) {
        try {
            //准备Request
            GetRequest request = new GetRequest("article", articleId.toString());
            //发送请求
            GetResponse response = restHighLevelClient.get(request, RequestOptions.DEFAULT);
            //解析响应结果
            String json = response.getSourceAsString();
            Articles articles = JSON.parseObject(json, Articles.class);
            return R.ok().data("article", articles);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    //用户修改头像或昵称，批量更新数据
    @Transactional
    @Override
    public R updateNicknameOrAvatar(QueryUser queryUser) {
        try {
            //获取文章列表
            List<Articles> articlesList = articleClient.getUserArticleListToES(queryUser.getId());
            for (Articles articles : articlesList) {
                //准备Request
                UpdateRequest request = new UpdateRequest("article", articles.getId().toString());
                //准备请求参数
                request.doc(JSONObject.toJSONString(articles), XContentType.JSON);
                //发送请求
                restHighLevelClient.update(request, RequestOptions.DEFAULT);
            }
            return R.ok().message("修改成功");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    // 封装响应结果
    private PageResult handleResponse(SearchResponse response) {
        // 4.解析响应
        SearchHits searchHits = response.getHits();
        // 4.1.总条数
        long total = searchHits.getTotalHits().value;
        // 4.2.获取文档数组
        SearchHit[] hits = searchHits.getHits();
        // 4.3.遍历
        List<Articles> articlesList = new ArrayList<>();
        for (SearchHit hit : hits) {
            // 4.3.1.获取source
            String json = hit.getSourceAsString();
            // 4.3.2.反序列化，非高亮的
            Articles articles = JSON.parseObject(json, Articles.class);
            // 设置高亮字段
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            if (!CollectionUtils.isEmpty(highlightFields)) {
                // 根据字段名获取高亮结果
                HighlightField titleHighlightField = highlightFields.get("title");
                HighlightField labelTranslationHighlightField = highlightFields.get("labelTranslation");
                if (titleHighlightField != null) {
                    // 获取高亮值
                    String title = titleHighlightField.getFragments()[0].string();
                    // 覆盖非高亮结果
                    articles.setTitle(title);
                }
                if (labelTranslationHighlightField != null) {
                    // 获取高亮值
                    String labelTranslation = labelTranslationHighlightField.getFragments()[0].string();
                    // 覆盖非高亮结果
                    articles.setLabelTranslation(labelTranslation);
                }
            }
            articlesList.add(articles);
        }
        // 4.4.封装返回
        return new PageResult(total, articlesList);
    }
}
